package com.tao.update;

import android.database.sqlite.SQLiteStatement;
import android.util.Log;

import com.tao.utils.ReflectUtil;

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.Property;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import org.greenrobot.greendao.internal.SqlUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
 * reate by tao on{DATE}
 */
public abstract class BaseUpdataWraper<T, K> {
    AbstractDao<T, K> dao;
    Property[] permKeys ;
    public BaseUpdataWraper(AbstractDao<T, K> dao ,Property[] permKeys) {
        this.dao = dao;
        this.permKeys = permKeys;
    }

    public void updateByWhere(T entity,Property[] wherePrps) {
        String tablename = dao.getTablename();
        String[] allColumns = dao.getAllColumns();
        String[] pkColumns = getWhereArgs(wherePrps);
        Database db = dao.getDatabase();

        String[] updateColumns = rempveAutoKeys(permKeys , allColumns);
        String sql = SqlUtils.createSqlUpdate(tablename, updateColumns, pkColumns);
        Log.i("greendao", "UpdateStatement sql:" + sql);
        DatabaseStatement stmt = db.compileStatement(sql);
        try {
            Object result = ReflectUtil.callMethod(dao, "assertSinglePk");
            if (db.isDbLockedByCurrentThread()) {
                synchronized (stmt) {
                    boolean isStandardSQLite = (boolean) ReflectUtil.getFieldValue(dao, "isStandardSQLite");
                    if (isStandardSQLite) {
                        updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(),  updateColumns,true ,wherePrps);
                        result = ReflectUtil.callMethod(dao, "updateInsideSynchronized", new Class[]{entity.getClass(), SQLiteStatement.class, boolean.class}, entity, stmt.getRawStatement(), true);
                        updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), updateColumns, true, wherePrps);
    
                    } else {
    //                    dao.updateInsideSynchronized(entity, stmt, true);
                        result = ReflectUtil.callMethod(dao, "updateInsideSynchronized", new Class[]{entity.getClass(), SQLiteStatement.class, boolean.class}, entity, stmt.getRawStatement(), true);
                        updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), updateColumns, true, wherePrps);
    
                    }
                }
            } else {
                // Do TX to acquire a connection before locking the stmt to avoid deadlocks
                db.beginTransaction();
                try {
                    synchronized (stmt) {
                        updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), updateColumns, true, wherePrps);
                    }
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
            }
        } finally {
            stmt.close();
        }
    }

    @NotNull
    private String[] getWhereArgs(Property[] wherePrps) {
        String[] pkColumns = new String[wherePrps.length];
        for (int i = 0; i < wherePrps.length; i++) {
            pkColumns[i] = wherePrps[i].columnName;
        }
        return pkColumns;
    }

    private void updateInsideSynchronized(T entity, SQLiteStatement stmt, String[] updateColumns, boolean lock, Property[] pkColumns) {
        bindUpdateValues(stmt, entity ,updateColumns);
        bindWheres(stmt,entity,pkColumns,updateColumns.length);
        K key = (K) ReflectUtil.callMethod(dao,"getKey", new Class[]{ entity.getClass()},entity);
        stmt.execute();
        ReflectUtil.callMethod(dao,"attachEntity", new Class[]{ key.getClass(),entity.getClass(),boolean.class},key, entity, lock);
    }

    private void bindWheres(SQLiteStatement stmt, T entity, Property[] pkColumns, int length) {
        for (int i = 0; i < pkColumns.length; i++) {
                bindWheres(i+length+1,entity,stmt,pkColumns[i]);            
        }
    }

    private void bindUpdateValues(SQLiteStatement stmt, T entity, String[] updateColumns) {
        for (int i = 0; i < updateColumns.length; i++) {
            bindUpdateValues(i+1 ,stmt ,entity);
        }
    }


    private String[] rempveAutoKeys(Property[] properties, String[] allColumns) {
        List<String> coloums = new ArrayList<>();

        for (String c : allColumns) {
            coloums.add(c);
            for (Property property : properties) {
                if (property.columnName.equals(c)) {
                    coloums.remove(c);
                }
            }
        }
        return coloums.toArray(new String[coloums.size()]);
    }
    public abstract void bindWheres(int i, T entity, SQLiteStatement stmt, Property pkColumn) ;

     public abstract void bindUpdateValues(int i, SQLiteStatement stmt, T entity)  ;


}
